package edu.zju.gis.dldsj.type;

import java.math.BigDecimal;
import java.math.MathContext;

/**
 * @author yanlo yanlong_lee@qq.com
 * @version 1.0 2018/07/12
 * Comments 一行记录（文本、结构化数据）
 */
public class Row {
    private StructType structType;
    private String[] values;

    public Row(int size) {
        this.values = new String[size];
    }

    public Row(String[] values) {
        this.values = values;
    }

    public Row(StructType structType, String[] values) {
        this.structType = structType;
        this.values = values;
    }

    public StructType getStructType() {
        return structType;
    }

    public void setStructType(StructType structType) {
        this.structType = structType;
    }

    public String[] getValues() {
        return values;
    }

    @Override
    public String toString() {
        return String.join("|", values);
    }

    public String toString(String delimiter) {
        return String.join(delimiter, values);
    }

    public void set(int i, Object value) {
        this.values[i] = value.toString();
    }

    public Object get(int i) {
        return values[i];
    }

    public int getSize() {
        return values.length;
    }

    public Integer getAsInt(int i) {
        if (structType != null) {
            StructField field = structType.getField(i);
            switch (field.getDataType()) {
                case INT:
                    if (!values[i].trim().isEmpty()) {
                        values[i] = values[i].trim();
                        int value;
                        if (values[i].matches("-*") || values[i].equals("--/--"))
                            value = 0;
                        else
                            value = (int) Double.parseDouble(values[i]);
                        return value;
                    } else if (field.isNullable() && field.getDefaultValue() == null)
                        return 0;//todo null
                    else
                        return (int) field.getDefaultValue();
                default:
                    throw new NumberFormatException();
            }
        } else
            return Integer.parseInt(values[i]);
    }

    public Integer getAsInt(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsInt(structType.getFieldIndex(fieldName));
    }

    public Long getAsLong(int i) {
        if (structType != null) {
            StructField field = structType.getField(i);
            switch (field.getDataType()) {
                case INT:
                case LONG:
                    if (!values[i].trim().isEmpty())
                        return (long) Double.parseDouble(values[i]);
                    else if (field.isNullable() && field.getDefaultValue() == null)
                        return null;
                    else
                        return (long) field.getDefaultValue();
                default:
                    throw new NumberFormatException();
            }
        } else
            return Long.parseLong(values[i]);
    }

    public Long getAsLong(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsLong(structType.getFieldIndex(fieldName));
    }

    public Float getAsFloat(int i) {
        if (structType != null) {
            StructField field = structType.getField(i);
            switch (field.getDataType()) {
                case INT:
                case LONG:
                case FLOAT:
                    if (!values[i].trim().isEmpty())
                        return (float) Double.parseDouble(values[i]);
                    else if (field.isNullable() && field.getDefaultValue() == null)
                        return null;
                    else
                        return (float) field.getDefaultValue();
                default:
                    throw new NumberFormatException();
            }
        } else
            return Float.parseFloat(values[i]);
    }

    public Float getAsFloat(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsFloat(structType.getFieldIndex(fieldName));
    }

    public Double getAsDouble(int i) {
        if (structType != null) {
            StructField field = structType.getField(i);
            switch (field.getDataType()) {
                case INT:
                case LONG:
                case FLOAT:
                case DOUBLE:
                case DECIMAL:
                    if (!values[i].trim().isEmpty())
                        return Double.parseDouble(values[i]);
                    else if (field.isNullable() && field.getDefaultValue() == null)
                        return null;
                    else
                        return (double) field.getDefaultValue();
                default:
                    throw new NumberFormatException();
            }
        } else
            return Double.parseDouble(values[i]);
    }

    public Double getAsDouble(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsDouble(structType.getFieldIndex(fieldName));
    }

    public Boolean getAsBoolean(int i) {
        if (structType != null) {
            StructField field = structType.getField(i);
            switch (field.getDataType()) {
                case BOOLEAN:
                    if (!values[i].trim().isEmpty())
                        return Boolean.parseBoolean(values[i]);
                    else if (field.isNullable() && field.getDefaultValue() == null)
                        return null;
                    else
                        return (boolean) field.getDefaultValue();
                default:
                    return false;
            }
        } else
            return Boolean.parseBoolean(values[i]);
    }

    public Boolean getAsBoolean(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsBoolean(structType.getFieldIndex(fieldName));
    }

    /**
     * 无限精度
     */
    public BigDecimal getAsBigDecimal(int i) {
        return getAsBigDecimal(i, MathContext.UNLIMITED);
    }

    public BigDecimal getAsBigDecimal(int i, MathContext mc) {
        if (structType != null) {
            StructField field = structType.getField(i);
            switch (field.getDataType()) {
                case DECIMAL:
                    if (!values[i].trim().isEmpty()) {
                        try {
                            return new BigDecimal(values[i], mc);
                        } catch (Exception e) {
                            return new BigDecimal(-0.0, mc);
                        }
                    } else if (field.isNullable() && field.getDefaultValue() == null)
                        return new BigDecimal(0);//todo null
                    else
                        return (BigDecimal) field.getDefaultValue();
                default:
                    throw new NumberFormatException();
            }
        } else
            return new BigDecimal(values[i], mc);
    }

    public BigDecimal getAsBigDecimal(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsBigDecimal(structType.getFieldIndex(fieldName));
    }

    public BigDecimal getAsBigDecimal(String fieldName, MathContext mc) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsBigDecimal(structType.getFieldIndex(fieldName), mc);
    }

    public Character getAsChar(int i) {
        if (values[i].length() > 1)
            throw new RuntimeException("got an data type transformation error at runtime");
        if (structType != null) {
            StructField field = structType.getField(i);
            switch (field.getDataType()) {
                case BOOLEAN:
                    if (!values[i].isEmpty())
                        return values[i].charAt(0);
                    else if (field.isNullable() && field.getDefaultValue() == null)
                        return null;
                    else
                        return (char) field.getDefaultValue();
                default:
                    throw new UnsupportedOperationException();
            }
        } else
            return values[i].charAt(0);
    }

    public Character getAsChar(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsChar(structType.getFieldIndex(fieldName));
    }

    //todo 交换方法名
    public String getAsString(int i) {
        if (structType != null) {
            StructField field = structType.getField(i);
            switch (field.getDataType()) {
                case CHAR:
                case STRING:
                    if (!values[i].isEmpty())
                        return values[i];
                    else if (field.isNullable() && field.getDefaultValue() == null)
                        return null;
                    else
                        return (String) field.getDefaultValue();
                case DECIMAL:
                    if (!values[i].isEmpty())
                        return values[i];
                    else if (field.isNullable() && field.getDefaultValue() == null)
                        return null;
                    else
                        return field.getDefaultValue().toString();
                default:
                    throw new UnsupportedOperationException();
            }
        } else
            return values[i];
    }

    public String getAsString(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getAsString(structType.getFieldIndex(fieldName));
    }

    public String getString(int i) {
        return values[i];
    }

    public String getString(String fieldName) {
        if (structType == null)
            throw new UnsupportedOperationException();
        else
            return getString(structType.getFieldIndex(fieldName));
    }

    public Object get(String name) throws NoSuchFieldException {
        if (structType == null)
            throw new UnsupportedOperationException("not structType is specified for this row");
        else {
            StructField structField = structType.getField(name);
            int index = structType.getFields().indexOf(structField);
            DataType dataType = structField.getDataType();
            switch (dataType) {
                case INT:
                    return getAsInt(index);
                case LONG:
                    return getAsLong(index);
                case FLOAT:
                    return getAsFloat(index);
                case DOUBLE:
                    return getAsDouble(index);
                case DECIMAL:
                    return getAsBigDecimal(index);
                case BOOLEAN:
                    return getAsBoolean(index);
                case CHAR:
                    return getAsChar(index);
                default:
                    return values[index];
            }
        }
    }
}
